package com.ccp.dev.workflow.service;

import com.ccp.dev.core.basic.consts.SystemConst;
import com.ccp.dev.core.basic.engine.GroovyScriptEngine;
import com.ccp.dev.core.basic.engine.IScript;
import com.ccp.dev.core.basic.util.ContextUtil;
import com.ccp.dev.core.basic.util.UUIDUtils;
import com.ccp.dev.core.basic.web.query.QueryFilter;
import com.ccp.dev.core.util.AppUtil;
import com.ccp.dev.core.util.BeanUtils;
import com.ccp.dev.core.util.StringPool;
import com.ccp.dev.core.util.StringUtil;
import com.ccp.dev.system.model.SysUser;
import com.ccp.dev.system.service.SysUserService;
import com.ccp.dev.workflow.bpmconst.BpmConst;
import com.ccp.dev.workflow.bpmutil.BpmUtil;
import com.ccp.dev.workflow.dao.*;
import com.ccp.dev.workflow.model.*;
import com.ccp.dev.workflow.model.bpm.BpmRunLog;
import com.ccp.dev.workflow.model.bpm.ProcessTask;
import com.ccp.dev.workflow.model.bpm.TaskExecutor;
import com.ccp.dev.workflow.service.bpm.BpmRunLogService;
import com.ccp.dev.workflow.service.bpm.thread.TaskThreadService;
import com.fasterxml.jackson.databind.node.ObjectNode;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.impl.cfg.IdGenerator;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.flowable.engine.*;
import org.flowable.engine.impl.RepositoryServiceImpl;
import org.flowable.engine.impl.persistence.entity.*;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.HistoricTaskInstanceEntity;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
import org.flowable.task.api.Task;
import org.flowable.ui.common.util.XmlUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.activity.ActivityRequiredException;
import javax.annotation.Resource;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * ClassDescribe:流程服务类
 * @author :jiayueqiao
 * Date: 2019-06-17
 * Since:1
 */
@Service
public class BpmService implements IScript {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private ActDefModelService actDefModelService;
    @Resource
    private ActGeByteArrayDao actGeByteArrayDao;
    @Resource
    private SpringProcessEngineConfiguration springProcessEngineConfiguration;
    @Resource
    private TaskService taskService;
    @Resource
    private TaskDao taskDao;
    @Resource
    private HistoryService historyService;
    @Resource
    private BpmNodeUserService bpmNodeUserService;
    @Resource
    private ProcessRunService processRunService;
    @Resource
    private TaskOpinionService taskOpinionService;
    @Resource
    private SysUserService sysUserService;
    @Resource
    private ActDefModelDao actDefModelDao;
    @Resource
    private ProcessRunDao processRunDao;
    @Resource
    private BpmFormRunDao bpmformRunDao;
    @Resource
    private HistoryActivityDao historyActivityDao;
    @Resource
    private HistoryProcessInstanceDao historyProcessInstanceDao;
    @Resource
    private ExecutionDao executionDao;
    @Resource
    private BpmProStatusDao bpmProStatusDao;
    @Resource
    private BpmProTransToDao bpmProTransToDao;
    @Resource
    private SysTemplateService sysTemplateService;
    @Resource
    private TaskMessageService taskMessageService;
    @Resource
    private TaskUserService taskUserService;
    @Resource
    private IdGenerator idGenerator;
    @Resource
    private TaskHistoryDao taskHistoryDao;
    @Resource
    private BpmRunLogService bpmRunLogService;
    @Resource
    private TaskUserDao taskUserDao;
    @Resource
    private BpmNodeSetService bpmNodeSetService;
    @Resource
    private TaskForkService taskForkService;

    private BpmnJsonConverter bpmnJsonConverter = new BpmnJsonConverter();

    private BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();




    /**
     * 设置分支节点条件，同时保存流程定义的xml和流程设计的xml文件。
     * @param modelId             流程定义ID
     * @param forkNode            网关节点
     * @param map                 条件映射。
     * @param canChoicePathNodeId 可以选择路径的节点，一般为之前网关节点之前的任务节点。
     * @throws IOException IOException
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveCondition(String modelId, String forkNode, Map<String, String> map, String canChoicePathNodeId) throws Exception {
        ActDefModel actDefModel = actDefModelService.getOneById(modelId);
        //更新该节点可选择的路径
        if (StringUtil.isNotEmpty(canChoicePathNodeId)) {
            actDefModel.getCanChoicePathNodeMap().put(forkNode, canChoicePathNodeId);
        } else {
            Map canChoicePathNodeMap = actDefModel.getCanChoicePathNodeMap();
            if (null != canChoicePathNodeMap) {
                Object object = canChoicePathNodeMap.get(forkNode);
                if (null != object) {
                    actDefModel.getCanChoicePathNodeMap().remove(forkNode);
                }
            }
        }
        actDefModel.updateCanChoicePath();

        String procDefId = actDefModel.getProcDefId();
        String deployId = actDefModel.getDeployId();
        String name = actDefModel.getName() + StringPool.SUFFIX_BPMN;
        ByteArrayEntityImpl byteArrayEntity = actGeByteArrayDao.getByteArrayEntityByNameAndDeploymentId(name, deployId);
        String defXml = new String(byteArrayEntity.getBytes(), StandardCharsets.UTF_8);
        defXml = BpmUtil.setCondition(forkNode, map, defXml);

        // 同步数据
        syncDataToModel(actDefModel, defXml);

        Map<String, Object> params = new HashMap<>(3);
        params.put("bytes", defXml.getBytes(StandardCharsets.UTF_8));
        params.put("name", name);
        params.put("deploymentId", deployId);
        actGeByteArrayDao.updateByteArrayByDeployId(params);
        springProcessEngineConfiguration.getProcessDefinitionCache().remove(procDefId);
        //查询流程定义，更新缓存
        ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(procDefId);
    }

    /**
     * 脚本任务节点保存脚本
     * @param nodeId 节点id
     * @param procDefId 流程定义id
     * @param script 脚本内容
     * @throws IOException IOException
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveScriptInModel(String nodeId, String procDefId, String script) throws Exception {
        ActDefModel actDefModel = actDefModelService.getByProcDefId(procDefId);
        String deployId = actDefModel.getDeployId();
        String name = actDefModel.getName() + StringPool.SUFFIX_BPMN;
        ByteArrayEntityImpl byteArrayEntity = actGeByteArrayDao.getByteArrayEntityByNameAndDeploymentId(name, deployId);
        String defXml = new String(byteArrayEntity.getBytes(), StandardCharsets.UTF_8);
        defXml = BpmUtil.setScriptInModel(nodeId, defXml, script);

        // 同步数据
        syncDataToModel(actDefModel, defXml);

        Map<String, Object> params = new HashMap<>(3);
        params.put("bytes", defXml.getBytes(StandardCharsets.UTF_8));
        params.put("name", name);
        params.put("deploymentId", deployId);
        actGeByteArrayDao.updateByteArrayByDeployId(params);
        springProcessEngineConfiguration.getProcessDefinitionCache().remove(procDefId);
        //查询流程定义, 更新缓存。
        ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(procDefId);
    }

    /**
     * 更新xml文件时将数据同步到模型的modelEditorJson中
     * @param actDefModel 流程模型
     * @param defXml 修改后的xml字符串
     * @throws XMLStreamException XMLStreamException
     */
    private void syncDataToModel(ActDefModel actDefModel, String defXml) throws XMLStreamException {
        XMLInputFactory xif = XmlUtil.createSafeXmlInputFactory();
        InputStream in = new ByteArrayInputStream(defXml.getBytes(StandardCharsets.UTF_8));
        InputStreamReader xmlIn = new InputStreamReader(in, StandardCharsets.UTF_8);
        XMLStreamReader xtr = xif.createXMLStreamReader(xmlIn);
        // 更新模型
        BpmnModel bpmnModel = bpmnXMLConverter.convertToBpmnModel(xtr);
        ObjectNode jsonNodes = bpmnJsonConverter.convertToJson(bpmnModel);
        String editorJson = jsonNodes.toString();
        actDefModel.setModelEditorJson(editorJson);
        actDefModelDao.update(actDefModel);
    }

    /**
     * 获取流程任务节点脚本
     * @param nodeId 节点id
     * @param modelId 流程模型id
     * @return 脚本
     * @throws IOException IOException
     */
    public String getScriptInModel(String nodeId, String modelId) throws IOException {
        ActDefModel actDefModel = actDefModelService.getOneById(modelId);
        String deployId = actDefModel.getDeployId();
        String name = actDefModel.getName() + StringPool.SUFFIX_BPMN;
        ByteArrayEntityImpl byteArrayEntity = actGeByteArrayDao.getByteArrayEntityByNameAndDeploymentId(name, deployId);
        String defXml = new String(byteArrayEntity.getBytes(), StandardCharsets.UTF_8);
        return BpmUtil.getScriptInModel(nodeId, defXml);
    }


    /**
     * 获取某个流程节点的所有节点（除nodeId节点）
     * @param bpmnModel 模型
     * @param nodeId 节点id
     * @return 节点map
     */
    public Map<String, String> getTaskNodes(BpmnModel bpmnModel, String nodeId) {
        Map<String, String> nodeMap = new HashMap<>(15);
        Map<String, Map<String, String>> bpmnMaps = BpmUtil.getActivities(bpmnModel);
        for (String keys : bpmnMaps.keySet()) {
            if ("开始节点".equals(keys) || "结束节点".equals(keys)|| "网关节点".equals(keys)) {
                continue;
            }
            Map<String, String> bpmnMap = bpmnMaps.get(keys);
            for (String key : bpmnMap.keySet()) {
                //移除nodeId节点
                if (key.equals(nodeId)) {
                    continue;
                }
                nodeMap.put(key,bpmnMap.get(key));
            }
        }
        return nodeMap;
    }

    /**
     * 获取所有的任务列表
     * @param queryFilter 过滤参数
     * @return list
     */
    public List<ProcessTask> getTasks(QueryFilter queryFilter) {
        List<ProcessTask> list = taskDao.queryList(queryFilter);
        return this.getAssignees(list);
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    public List<ProcessTask> getTasks(String processInstanceId) {
        List taskList = taskDao.getByInstanceId(processInstanceId);
        return this.getAssignees(taskList);
    }

    /**
     * 2019-11-08
     * 提取公共方法，处理任务列表中的执行人数据
     * @param list 任务list
     * @return list
     */
     private List<ProcessTask> getAssignees(List<ProcessTask> list){
         for(ProcessTask processTask: list){
             String id = processTask.getId();
             //获取任务执行人（非单一的）
             Set<TaskExecutor> candidateExecutors = taskUserService.getCandidateExecutors(id);
             processTask.setAssignees(candidateExecutors);
         }
         return list;
     }

    /**
     * 使用于流转的情况，根据任务id获取执行流对象
     * @param taskId
     * @return
     */
     public ExecutionEntity getExecutionByTaskId_TransTo(String taskId){
         TaskEntity taskEntity = getParentTask(taskId);
         if (taskEntity.getExecutionId() == null) {
             return null;
         }
         return getExecution(taskEntity.getExecutionId());
     }

    /**
     * 根据任务id获取顶级父级任务对象，适用于多级流转的情况
     * @param taskId
     * @return
     */
     public TaskEntity getParentTask(String taskId){
         TaskEntity taskEntity = getTask(taskId);
         //如果是流转中任务
         if (TaskOpinion.STATUS_TRANSTO_ING.toString().equals(taskEntity.getDescription())&&StringUtil.isNotEmpty(taskEntity.getParentTaskId())) {
             taskId = taskEntity.getParentTaskId();
             taskEntity = getParentTask(taskId);
         }
         return taskEntity;
     }
    /**
     * 根据任务id获取执行流对象
     * @param taskId 任务id
     * @return ExecutionEntity
     */
    public ExecutionEntity getExecutionByTaskId(String taskId) {
        TaskEntity taskEntity = getTask(taskId);
        if (taskEntity.getExecutionId() == null) {
            return null;
        }
        return getExecution(taskEntity.getExecutionId());
    }

    /**
     * 更新任务的执行人
     * @param taskId 任务id
     * @param userId 用户id
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateTaskAssignee(String taskId, String userId) {

        taskDao.updateTaskAssignee(taskId, userId);

    }

    /**
     * 仅完成任务，不作跳转处理。
     * <pre>
     * 1.获取流程活动实例。
     * 2.备份原来的跳转情况。
     * 3.清空流程跳转。
     * 4.完成当前任务。
     * 5.将备份的跳转再添加回来。
     * </pre>
     *
     * @param taskId 任务id
     */
    public void onlyCompleteTask(String taskId) {
        TaskEntity task = getTask(taskId);
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(task.getTaskDefinitionKey());
        List<SequenceFlow> listCopyier = new ArrayList<>();
        listCopyier.addAll(flowNode.getOutgoingFlows());
        flowNode.getOutgoingFlows().clear();
        taskService.complete(task.getId());
        flowNode.getOutgoingFlows().addAll(listCopyier);
    }

    /**
     * 清除起始节点后的连线，重新创建起始节点到某个节点的连线
     * @param procDefId 流程定义id
     * @param targetNodeId 目标节点
     */
    public void reDrawLink(String procDefId, String targetNodeId) {
        String start = NodeCache.getStartNode(procDefId).getNodeId();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(procDefId);
        FlowNode startFlowNode = (FlowNode) bpmnModel.getFlowElement(start);
        startFlowNode.getOutgoingFlows().clear();
        SequenceFlow sf = new SequenceFlow(start, targetNodeId);
        startFlowNode.getOutgoingFlows().add(sf);
    }


    /**
     * 待办任务列表
     * @return list
     */
    public List<ProcessTask> queryMyTask(QueryFilter queryFilter) {

        SysUser curUser = (SysUser) ContextUtil.getCurrentUser();
        queryFilter.put("userId", ContextUtil.getCurrentUserId());
        return taskDao.queryMyTask(queryFilter);

    }


    /**
     * 判断是否允许选择路径。
     * @param procDefId 定义id
     * @param taskId 任务id
     * @return boolean
     */
    public boolean getCanChoicePath(String procDefId, String taskId) {
        if (StringUtil.isEmpty(taskId)) {
            return false;
        }
        TaskEntity taskEntity = getTask(taskId);
        String taskKey = taskEntity.getTaskDefinitionKey();
        if (StringUtil.isEmpty(procDefId)) {
            procDefId = taskEntity.getProcessDefinitionId();
        }
        ActDefModel actDefModel = actDefModelService.getByProcDefId(procDefId);
        String canChoicePath = actDefModel.getCanChoicePath();
        if (StringUtil.isNotEmpty(canChoicePath)) {
            return canChoicePath.contains(":"+taskKey);
        } else {
            return false;
        }
    }

    /**
     * 获取起始节点的下一步执行人。
     *
     * @param procDefId   流程定义ID
     * @param startUserId 流程发起人id。
     * @return list
     * @throws Exception ex
     */
    public List<NodeTranUser> getStartNodeUserMap(String procDefId, String startUserId, Map<String, Object> vars) throws Exception {
        ActDefModel actDefModel = actDefModelService.getByProcDefId(procDefId);
        //是否跳过第一个节点
        boolean jumpOverFirstNode = actDefModel.getToFirstNode() == (short) 1;

        FlowNodeInfo startNode = NodeCache.getStartNode(procDefId);

        List<FlowNodeInfo> nextNodes = startNode.getNextFlowNodesInfo();

        if (nextNodes.size() != 1) {
            throw new Exception("开始节点后没有连接的节点!");
        }

        if (jumpOverFirstNode) {
            FlowNodeInfo flowNode = nextNodes.get(0);
            if (!FlowNodeInfo.TYPE_USERTASK.equals(flowNode.getNodeType())) {
                throw new Exception("第一个节点必须为任务节点!");
            }
            nextNodes = flowNode.getNextFlowNodesInfo();
        }


        return getNodeUserMap(procDefId, null, nextNodes, startUserId, startUserId, false, vars);
    }

    /**
     * 取得流程节点中的流程节点对应的执行人员列表
     *
     * @param procDefId 流程定义id
     * @param nextFlowNodes 当前节点的后续节点
     * @param curUserId 当前用户id
     * @param preUserId 之前用户id
     * @return list
     */
    public List<NodeTranUser> getNodeUserMap(String procDefId, String instanceId, List<FlowNodeInfo> nextFlowNodes, String curUserId, String preUserId, boolean canChoicePath, Map<String, Object> vars) {
        List<NodeTranUser> nodeList = new ArrayList<NodeTranUser>();
        for (FlowNodeInfo flowNode : nextFlowNodes) {
            Set<NodeUserMap> nodeUserMapSet = new LinkedHashSet<NodeUserMap>();
            NodeTranUser nodeTranUser = new NodeTranUser(flowNode.getNodeId(), flowNode.getNodeName(), nodeUserMapSet);

            //若为任务节点，即把任务节点对应的人员取出来
            if (flowNode.getNodeType().equals(FlowNodeInfo.TYPE_USERTASK)) {
                Set<TaskExecutor> taskExecutors = getNodeHandlerUsers(procDefId, instanceId, flowNode.getNodeId(), curUserId, preUserId, vars);
                nodeUserMapSet.add(new NodeUserMap(flowNode.getNodeId(), flowNode.getNodeName(), taskExecutors, flowNode.getIsMultiInstance()));
            } else if (flowNode.getNodeType().indexOf("Gateway") != -1) {
                //如果是条件网关类型，可以让用户在界面上选择执行的分支
                if (canChoicePath && FlowNodeInfo.TYPE_INCLUSIVEGATEWAY.equals(flowNode.getNodeType())) {
                    Map<String, String> nextPathMap = new HashMap<String, String>();
                    List<FlowNodeInfo> gatewayPathList = flowNode.getNextFlowNodesInfo();
                    for (FlowNodeInfo gatewayFlowNode : gatewayPathList) {
                        String nodeType = gatewayFlowNode.getNodeType();
                        if(nodeType.equals(FlowNodeInfo.TYPE_INCLUSIVEGATEWAY)){
                            nextPathMap.put(gatewayFlowNode.getNodeId(), gatewayFlowNode.getNodeName());
                            ActDefModel actDefModel = actDefModelService.getByProcDefId(procDefId);
                            String canChoicePathString = actDefModel.getCanChoicePath();
                            //若后边节点仍任是条件同步分支，且可以选择
                            setNextPathMap(canChoicePathString,nextPathMap,gatewayFlowNode);
                        }else{
                            nextPathMap.put(gatewayFlowNode.getNodeId(), gatewayFlowNode.getNodeName());
                        }
                        if (nodeType.equals(FlowNodeInfo.TYPE_CALLACTIVITY)) {
                            //外部子流程
                            Map<String, FlowNodeInfo> subGatewayChildNodes = gatewayFlowNode.getSubProcessNodes();
                            String subFlowKey = gatewayFlowNode.getAttribute("subFlowKey");
                            FlowNodeInfo startNode = NodeCache.getStartNode(subGatewayChildNodes);
                            //获取子流程
                            ActDefModel actDefModel = actDefModelService.getMainByDefKey(subFlowKey);
                            String subProcessDefinitionId = actDefModel.getProcDefId();
                            genUserMap(subProcessDefinitionId, instanceId, startNode.getNextFlowNodesInfo(), nodeUserMapSet, curUserId, preUserId, vars);
                        }
                    }
                    nodeTranUser.setNextPathMap(nextPathMap);
                }
                List<FlowNodeInfo> newNextFlowNodes = this.getNewFlowNodes(procDefId, flowNode, vars);
                genUserMap(procDefId, instanceId, newNextFlowNodes, nodeUserMapSet, curUserId, preUserId, vars);
            } else if ("subProcess".equals(flowNode.getNodeType()) && flowNode.getSubFirstNode() != null) {
                //若为子流程，则查找子流程中的开始节点后的任务作为后续的跳转节点
                genUserMap(procDefId, instanceId, flowNode.getSubFirstNode().getNextFlowNodesInfo(), nodeUserMapSet, curUserId, preUserId, vars);
            }
            //若外部子流程
            else if ("callActivity".equals(flowNode.getNodeType())) {
                Map<String, FlowNodeInfo> subChildNodes = flowNode.getSubProcessNodes();
                String subFlowKey = flowNode.getAttribute("subFlowKey");
                FlowNodeInfo startNode = NodeCache.getStartNode(subChildNodes);
                //获取子流程
                ActDefModel actDefModel = actDefModelService.getMainByDefKey(subFlowKey);
                String subProcessDefinitionId = actDefModel.getProcDefId();
                //在变量中临时添加父流程定义ID
                vars.put(BpmConst.FLOW_PARENT_PROCDEFID, procDefId);
                genUserMap(subProcessDefinitionId, instanceId, startNode.getNextFlowNodesInfo(), nodeUserMapSet, curUserId, preUserId, vars);
            }
            nodeList.add(nodeTranUser);
        }
        return nodeList;
    }

    private void setNextPathMap(String canChoicePathString,Map<String, String> nextPathMap,FlowNodeInfo flowNodeInfo){
        String nodeType = flowNodeInfo.getNodeType();
        if(nodeType.equals(FlowNodeInfo.TYPE_INCLUSIVEGATEWAY)){
            nextPathMap.put(flowNodeInfo.getNodeId(), flowNodeInfo.getNodeName());
            //若后边节点仍任是条件同步分支，且可以选择
            if(canChoicePathString.contains(flowNodeInfo.getNodeId()+":")){
                List<FlowNodeInfo> nextFlowNodesInfo =flowNodeInfo.getNextFlowNodesInfo();
                for (FlowNodeInfo gatewayFlowNode : nextFlowNodesInfo) {
                    setNextPathMap(canChoicePathString,nextPathMap,gatewayFlowNode);
                }
            }
        }else{
            nextPathMap.put(flowNodeInfo.getNodeId(), flowNodeInfo.getNodeName());
        }
    }

    /**
     * 取得流程节点中的流程节点对应的执行人员列表
     * @param processDefinitionId 定义id
     * @param nextFlowNodes 下一个节点
     * @param nodeUserMapSet 用户设置
     * @param curUserId 当前用户
     * @param preUserId 之前用户
     */
    private void genUserMap(String processDefinitionId, String instanceId, List<FlowNodeInfo> nextFlowNodes, Set<NodeUserMap> nodeUserMapSet, String curUserId, String preUserId, Map<String, Object> vars) {
        for (FlowNodeInfo flowNode : nextFlowNodes) {
            if (flowNode.getNodeType().indexOf("Gateway") != -1) {
                //网关
                List<FlowNodeInfo> newNextFlowNodes = this.getNewFlowNodes(processDefinitionId, flowNode, vars);
                genUserMap(processDefinitionId, instanceId, newNextFlowNodes, nodeUserMapSet, curUserId, preUserId, vars);
            } else if (flowNode.getNodeType().indexOf("subProcess") != -1) {
                //内部子流程
                genUserMap(processDefinitionId, instanceId, flowNode.getSubFirstNode().getNextFlowNodesInfo(), nodeUserMapSet, curUserId, preUserId, vars);
            } else if (FlowNodeInfo.TYPE_USERTASK.equals(flowNode.getNodeType())) {
                Set<TaskExecutor> taskExecutors = getNodeHandlerUsers(processDefinitionId, instanceId, flowNode.getNodeId(), curUserId, preUserId, vars);
                NodeUserMap nodeUserMap = new NodeUserMap(flowNode.getNodeId(), flowNode.getNodeName(), taskExecutors, flowNode.getIsMultiInstance());
                boolean isRepeat = false;
                for (NodeUserMap nodeUser : nodeUserMapSet) {
                    if (nodeUser.getNodeId().equals(nodeUserMap.getNodeId())) {
                        isRepeat = true;
                    }
                }

                if (!isRepeat) {
                    nodeUserMapSet.add(nodeUserMap);
                }
            }
        }
    }

    /**
     * 取至某个流程定义某个节点上的执行人员
     *
     * @param procDefId   流程定义ID
     * @param instanceId  流程定义实例
     * @param nodeId      节点ID
     * @param startUserId 启动用户
     * @return set
     */
    public Set<TaskExecutor> getNodeHandlerUsers(String procDefId, String instanceId, String nodeId, String startUserId, String preUserId, Map<String, Object> vars) {
        Set<TaskExecutor> uSet = new LinkedHashSet<>();
        List<TaskExecutor> taskExecutorList = bpmNodeUserService.getExeUserIds(procDefId, instanceId, nodeId, startUserId, preUserId, vars);
        if (taskExecutorList == null) {
            return uSet;
        }
        for (TaskExecutor taskExecutor : taskExecutorList) {
            uSet.add(taskExecutor);
        }
        return uSet;
    }

    /**
     * 获取flowNode节点的可执行的后续节点列表，用于排他网关
     *
     * @param processDefinitionId 流程定义ID
     * @param flowNode            节点
     * @param vars                流程参数
     * @return list
     */
    private List<FlowNodeInfo> getNewFlowNodes(String processDefinitionId, FlowNodeInfo flowNode, Map<String, Object> vars) {
        List<FlowNodeInfo> newFlowNodes = new ArrayList();
        //排他网关分支去判断可执行的条件
        if (flowNode.getNodeType().equals(FlowNodeInfo.TYPE_EXCLUSIVEGATEWAY)) {
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
            ExclusiveGateway gateway = (ExclusiveGateway) bpmnModel.getMainProcess().getFlowElement(flowNode.getNodeId());
            //获取所有线路信息
            List<SequenceFlow> outTransitions = gateway.getOutgoingFlows();
            for (SequenceFlow tr : outTransitions) {
                String groovyScript = tr.getConditionExpression();
                groovyScript = StringUtil.trimPrefix(groovyScript, "${");
                groovyScript = StringUtil.trimSufffix(groovyScript, "}");
                GroovyScriptEngine engine = AppUtil.getBean(GroovyScriptEngine.class);
                boolean result = engine.executeBoolean(groovyScript, vars);
                if (result) {
                    String nodeId = tr.getTargetRef();
                    for (FlowNodeInfo next : flowNode.getNextFlowNodesInfo()) {
                        if (next.getNodeId().equals(nodeId)) {
                            newFlowNodes.add(next);
                        }
                    }
                }
            }
        } else {
            newFlowNodes = flowNode.getNextFlowNodesInfo();
        }
        return newFlowNodes;
    }

    /**
     * 取到当前任务的下一步的节点及其映射人员
     *
     * @param taskId    任务ID
     * @param preUserId 前一用户ID
     * @return list
     */
    public List<NodeTranUser> getNodeTaskUserMap(String taskId, String preUserId, boolean canChoicePath) {
        TaskEntity taskEntity = getTask(taskId);
        //获取流程实例id
        String instanceId = taskEntity.getProcessInstanceId();
        String procDefId = taskEntity.getProcessDefinitionId();
        //从节点缓存中获取节点数据。
        FlowNodeInfo flowNode = NodeCache.getByProcDefId(procDefId).get(taskEntity.getTaskDefinitionKey());

        ProcessRun processRun = processRunService.getByProcInstanceId(instanceId);

        String startUserId = "";
        //获取任务发起人和发起人组织。
        if (processRun != null) {
            startUserId = processRun.getCreatorId();
        }
        Map<String, Object> vars = taskService.getVariables(taskId);
        vars.put(BpmConst.EXECUTION_ID_, taskEntity.getExecutionId());

        return getNodeUserMap(procDefId, instanceId, flowNode.getNextFlowNodesInfo(), startUserId, preUserId, canChoicePath, vars);
    }



    /**
     * 返回流程历史
     * @param procInstanceId 流程实例id
     * @param nodeId 节点id
     * @return HistoricTaskInstanceEntity
     */
    public HistoricTaskInstanceEntity getHistoricTaskInstanceEntity(String procInstanceId,String nodeId) {
        List<HistoricTaskInstance> list =  historyService.createHistoricTaskInstanceQuery().processInstanceId(procInstanceId).
                taskDefinitionKey(nodeId).orderByTaskCreateTime().desc().list();
        if(null!=list && list.size()>0){
            return (HistoricTaskInstanceEntity)list.get(0);
        }else{
            return null;
        }
    }

    /**
     * 根据任务id获取可以跳转的任务节点。
     * <pre>
     * 	数据结构解释。
     * 	该方法返回的数据结构为：
     * 	Map&lt;String, Map&lt;String, String>>
     * 	键可能的值为4个：任务节点,网关节点，结束节点,自动任务
     *  对应的值为一个MAP:
     *  键为任务id，值为任务名称。
     * </pre>
     *
     * @param taskId 任务id
     * @return map
     */
    public Map<String, Map<String, String>> getJumpNodes(String taskId) {
        //取到当前正在活动的节点
        List<String> actIds = getActiveActIdsByTaskId(taskId);
        TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(taskEntity.getProcessDefinitionId());
        Map<String, Map<String, String>> map = BpmUtil.getTranstoActivitys(bpmnModel, actIds, true);
        return map;
    }

    /**
     * 按任务获取其所有所有的运行节点ID
     * @param taskId 任务id
     * @return list
     */
    public List<String> getActiveActIdsByTaskId(String taskId) {
        TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
        return getActiveActIdsByProcessInstanceId(taskEntity.getProcessInstanceId());
    }

    /**
     * 按流程实例获取其所有的运行节点ID
     * @param processInstanceId 实例id
     * @return list
     */
    public List<String> getActiveActIdsByProcessInstanceId(String processInstanceId) {
        List<String> acts = new ArrayList<>();
        List<ProcessTask> taskList = getTasks(processInstanceId);
        for (ProcessTask entity : taskList) {
            acts.add(entity.getTaskDefinitionKey());
        }
        return acts;
    }

    /**
     * 获取流程任务审批状态
     * @param procInstId 流程实例ID 前端传过来的
     * @param taskKey    节点id 节点的key
     * @return TaskNodeStatus
     */
    public TaskNodeStatus getNodeCheckStatusInfo(String procInstId, String taskKey) {
        TaskNodeStatus taskNodeStatus = new TaskNodeStatus();
        //根据实例id 节点id 获取审批意见列表  对应意见表中的字段是taskkey 以及actinstid（可重复）
        List<TaskOpinion> taskOpinionList = taskOpinionService.getByProcInstIdTaskKey(procInstId, taskKey);
        if (taskOpinionList.size() != 0) {
            Collections.sort(taskOpinionList, new Comparator<TaskOpinion>() {
                @Override
                public int compare(TaskOpinion o1, TaskOpinion o2) {
                    //正序排列  起始事件晚的在后，也就是最新opinion
                    return o1.getStartTime().compareTo(o2.getStartTime());
                }
            });
            //倒序排列，
            Collections.reverse(taskOpinionList);
            taskNodeStatus.setProcInstId(procInstId);
            taskNodeStatus.setTaskKey(taskKey);
            taskNodeStatus.setTaskOpinionList(taskOpinionList);

            TaskOpinion opinion = taskOpinionList.get(taskOpinionList.size() - 1);
            //设置最后审批状态  审批状态 值来自TaskOpinion中的STATUS的常量
            //	/*
            //	 * 1=同意
            //	   2=反对
            //	   3=驳回
            //	   0=弃权
            //	   -1=正在审批
            //	   -2=尚未审批
            //	*/
            taskNodeStatus.setLastCheckStatus(opinion.getCheckStatus());
        } else {
            ProcessRun processRun;
            processRun= processRunService.getByProcInstanceId(procInstId);
            if(processRun==null) {
                processRun= processRunService.getByProcInstanceIdFromHis(procInstId);
            }
            //2020-01-03 jyq添加，当为流程草稿时processRun为null
            if (null!=processRun && processRun.getStatus().equals(ProcessRun.STATUS_RUNNING)) {
                Map<String, Object> vars = runtimeService.getVariables(procInstId);
                //获取执行用的
                Object nodeUserObj = vars.get(BpmConst.NODE_USER_ASSIGNEE);
                String nodeUserJson = "";
                if (nodeUserObj != null) {
                    nodeUserJson = String.valueOf(nodeUserObj);
                }

                List<TaskExecutor> taskExecutorList= new ArrayList<>();
                if (StringUtil.isNotEmpty(nodeUserJson)) {
                    JSONArray jsonArray = JSONArray.fromObject(String.valueOf(nodeUserJson));
                    for (Object obj : jsonArray) {
                        JSONObject object = (JSONObject) obj;
                        String setNodeId = object.getString("nodeId");
                        String setUserId = object.getString("userId");
                        String setUserName = object.getString("userName");
                        if (taskKey.equals(setNodeId)) {
                            taskExecutorList.add(TaskExecutor.getTaskUser(setUserId,setUserName));
                        }
                    }
                }else{
                    taskExecutorList = bpmNodeUserService.getExeUserIds(processRun.getProcDefId(), procInstId, taskKey,
                            (String) vars.get("startUser"), (String) vars.get("startUser"), vars);
                }

                taskNodeStatus.setTaskExecutorList(taskExecutorList);
            }
        }
        return taskNodeStatus;
    }

    /**
     * 根据流程实例ID终结流程。
     * 1.更新bpm_pro_run状态为结束。
     * 2.删除act_ru_identitylink数据。
     * 3.删除任务表  ACT_RU_TASK。
     * 4.更新节点实例。
     * update ACT_HI_ACTINST set EXECUTION_ID_ = ?,
     * ASSIGNEE_ = ?, END_TIME_ = ?, DURATION_ = ? where ID_ = ?
     * 5.更新历史实例。
     * update ACT_HI_PROCINST set PROC_DEF_ID_ = ?, START_TIME_ = ?, END_TIME_ =
     * ?, DURATION_ = ?, END_ACT_ID_ = ?, DELETE_REASON_ = ? where ID_ = ?
     * 6. 删除流程变量。
     * ACT_RU_VARIABLE
     * 7. 删除execution 表。
     * ACT_RU_EXECUTION。
     * 删除流程表单运行情况 BPM_FORM_RUN
     *
     * @param instanceId 实例id
     * @throws Exception 异常
     */
    @Transactional(rollbackFor = Exception.class)
    public void endProcessByInstanceId(String instanceId, String nodeId,
                                             String memo) throws Exception {
        SysUser sysUser = (SysUser) ContextUtil.getCurrentUser();
        if(null==sysUser){
            sysUser = new SysUser();
            sysUser.setUserId(SystemConst.SYSTEMUSERID);
            sysUser.setFullName(SystemConst.SYSTEMUSERNAME);
        }
        // 更新流程执行状态。
        ProcessRun processRun = processRunService.getByProcInstanceId(instanceId);
        processRun.setStatus(ProcessRun.STATUS_MANUAL_FINISH);
        processRun.setEndTime(new Date());
        String startUserId = processRun.getCreatorId();
        SysUser startUser = sysUserService.getById(startUserId);

        ActDefModel bpmDefinition = actDefModelDao.getByProcDefId(processRun.getProcDefId());
        long duration = processRunService.getProcessDuration(processRun);
        long lastSubmitDuration = processRunService.getProcessLastSubmitDuration(processRun);
        processRun.setDuration(duration);
        processRun.setLastSubmitDuration(lastSubmitDuration);

        // 更新历史
        processRunDao.updateHistory(processRun);
        // 删除流程实例
        processRunDao.delById(processRun.getRunId());

        // 删除候选人
        taskDao.delCandidateByInstanceId(instanceId);
        // 根据流程实例删除流程。
        taskDao.delByInstanceId(instanceId);
        //删除流程表单运行情况
        bpmformRunDao.delByInstanceId(String.valueOf(instanceId));
        // 更新历史实例。
        updHistoryProcInst(instanceId, nodeId, sysUser.getUserId());
        // 更新流程实例
        HistoricProcessInstanceEntity processInstance = (HistoricProcessInstanceEntity) historyService
                .createHistoricProcessInstanceQuery()
                .processInstanceId(instanceId).singleResult();
        processInstance.setEndTime(new Date());
        processInstance.setDurationInMillis(System.currentTimeMillis()
                - processInstance.getStartTime().getTime());
        processInstance.setEndActivityId(nodeId);
        historyProcessInstanceDao.update(processInstance);
        // 根据流程实例删除变量
        executionDao.delVariableByProcInstId(instanceId);
        // 根据流程实例删除excution。
        executionDao.delExecutionByProcInstId(instanceId);

        Map<String, Object> stringObjectMap = new HashMap<>(3);
        stringObjectMap.put("procInstId",instanceId);
        stringObjectMap.put("nodeId",nodeId);
        stringObjectMap.put("status",TaskOpinion.STATUS_ENDPROCESS);
        bpmProStatusDao.updStatus(stringObjectMap);
        //删除流转状态记录
        bpmProTransToDao.delByProcInstId(instanceId);
        // 更新流程审批历史。
        updateTaskOpinion(instanceId, memo, sysUser);
        String informStart = bpmDefinition.getInformStart();
        List<SysUser> receiverUserList = new ArrayList<>();
        receiverUserList.add(startUser);
        Map<String, String> msgTempMap = sysTemplateService.getTempByFun(SysTemplate.USE_TYPE_TERMINATION);
        if (StringUtil.isNotEmpty(informStart)) {
            taskMessageService.sendMessage(sysUser, receiverUserList, informStart, msgTempMap, processRun.getSubject(), memo, null, processRun.getRunId(), null);
        }
        memo = "结束流程:" + processRun.getSubject() + ",结束原因:" + memo;
        bpmRunLogService.addRunLog(processRun.getRunId(), BpmRunLog.OPERATOR_TYPE_ENDTASK, memo);

    }

    /**
     * 根据流程定义ID和节点Id判断当前节点是否是会签节点。
     *
     * @param procDefId 流程定义ID。
     * @param nodeId   节点id。
     * @return boolean
     */
    public boolean isSignTask(String procDefId, String nodeId) {
        if (StringUtil.isEmpty(procDefId) || StringUtil.isEmpty(nodeId)) {
            return false;
        }
        return NodeCache.isSignTaskNode(procDefId, nodeId);
    }

    /**
     * 根据节点id获取节点元素
     * @param procDefId 流程定义id
     * @param nodeId 节点id
     * @return FlowElement
     */
    public FlowElement getFlowElement(String procDefId, String nodeId) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(procDefId);
        Process mainProcess = bpmnModel.getMainProcess();
        for (FlowElement node : mainProcess.getFlowElements()) {
            if (node.getId().equals(nodeId)) {
                return node;
            }
            if (node instanceof SubProcess) {
                Collection<FlowElement> subElements = ((SubProcess) node).getFlowElements();
                for (FlowElement subElement : subElements) {
                    if (subElement.getId().equals(nodeId)) {
                        return subElement;
                    }
                }

            }
        }
        return null;
    }

    /**
     * 更新流程实例列表。
     * 1、更新流程的执行时间
     * 2、执行人
     * @param procInstId 实例id
     * @param nodeId 节点id
     * @param assignee 分配人id
     */
    @Transactional(rollbackFor = Exception.class)
    public void updHistoryProcInst(String procInstId, String nodeId, String assignee) {
        List<HistoricActivityInstanceEntity> hisList = historyActivityDao.getByInstanceId(procInstId, nodeId);
        for (HistoricActivityInstanceEntity hisActInst : hisList) {
            Date endTime = new Date();

            hisActInst.setEndTime(endTime);
            hisActInst.setDurationInMillis(System.currentTimeMillis()
                    - hisActInst.getStartTime().getTime());
            //任务执行人为空或者 执行人和当前人不一致，设置当前人为任务执行人。
            if (StringUtil.isEmpty(hisActInst.getAssignee()) || !hisActInst.getAssignee().equals(assignee)) {
                hisActInst.setAssignee(assignee);
            }
            historyActivityDao.update(hisActInst);
        }
    }


    /**
     * 更新流程意见为结束。
     * @param instanceId 实例id
     */
    private void updateTaskOpinion(String instanceId, String memo, SysUser sysUser) {
        List<TaskOpinion> list = taskOpinionService.getCheckOpinionByInstId(instanceId);
        for (TaskOpinion taskOpion : list) {
            taskOpion.setCheckStatus(TaskOpinion.STATUS_ENDPROCESS);
            taskOpion.setExeUserId(sysUser.getUserId());
            taskOpion.setExeFullname(sysUser.getFullName());
            taskOpion.setEndTime(new Date());
            taskOpion.setOpinion(memo);
            Long duration = taskOpion.getEndTime().getTime() - taskOpion.getStartTime().getTime();
            taskOpion.setDurTime(duration);
            taskOpinionService.update(taskOpion);
        }
    }

    /**
     * 转移到指定的节点。<br>
     * 指定任务ID,转移到的节点，转移成功保存的相关情况。
     *
     * @param task    任务实体
     * @param toNode    转移到的节点id
     * @throws ActivityRequiredException
     */
    @Transactional(rollbackFor = Exception.class)
    public void transTo(TaskEntity task, String toNode) throws Exception{
        // 获取流程定义对象
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        FlowNode curActi = (FlowNode)bpmnModel.getFlowElement(task.getTaskDefinitionKey());
        List<SequenceFlow> sequenceFlowList = curActi.getOutgoingFlows();
        //将当前任务节点的后续节点备份
        List<SequenceFlow> sequenceFlowListCopier = new ArrayList<>();
        sequenceFlowListCopier.addAll(sequenceFlowList);
        //后续的节点设置
        BpmNodeSet bpmNodeSet = null;
        //是否需要去除当前任务的后续跳转线
        boolean isNeedRemoveTran = false;
        if (JumpRule.RULE_INVALID.equals(toNode)) {
            //仅完成当前任务
            curActi.getOutgoingFlows().clear();
            taskService.complete(task.getId());
            curActi.setOutgoingFlows(sequenceFlowListCopier);
            return;
        }else{
            if (StringUtils.isEmpty(toNode)) {
                //正常按定义规则跳转，查找后续节点是不是汇总任务节点
                for (SequenceFlow sequenceFlow : sequenceFlowList) {
                    String destActId = sequenceFlow.getTargetRef();
                    bpmNodeSet = bpmNodeSetService.getByProcDefIdJoinTaskKey(task.getProcessDefinitionId(), destActId);
                    if (bpmNodeSet != null) {
                        break;
                    }
                }
                if(null==bpmNodeSet){
                    //后续节点不是汇总节点，正常流转
                    //完成当前任务
                    taskService.complete(task.getId());
                    return;
                }else{
                    //后续节点是汇总节点
                    //是否需要清除后续的节点，只完成任务
                    isNeedRemoveTran = handForkTask(task,bpmNodeSet.getJoinTaskKey(),curActi);
                    if(isNeedRemoveTran){
                        //仅完成当前任务
                        curActi.getOutgoingFlows().clear();
                        taskService.complete(task.getId());
                        curActi.setOutgoingFlows(sequenceFlowListCopier);
                    }else{
                        taskService.complete(task.getId());
                    }
                }
            }else{
                //选择路径跳转或回退
                String procInstId = task.getProcessInstanceId();
                String nodeId = task.getTaskDefinitionKey();
                jumpToDestNode(procInstId,nodeId,toNode);
                // 更新历史表执行人信息
                updateAssigneeInfo(task);
            }
        }

    }

    /**
     * 更新历史表信息
     * @param task 任务
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateAssigneeInfo(TaskEntity task) {
        String taskId = task.getId();
        String nodeId = task.getTaskDefinitionKey();
        String executionId = task.getExecutionId();
        String userId = ContextUtil.getCurrentUserId();

        // 修改act_hi_taskinst表
        ProcessTaskHistory processTaskHistory = taskHistoryDao.getById(taskId);
        processTaskHistory.setAssignee(userId);
        taskHistoryDao.updateAssignee(processTaskHistory);

        // 修改act_hi_actinst.
        List<HistoricActivityInstanceEntity> historyActivityList = historyActivityDao.getByExecutionId(executionId, nodeId);
        HistoricActivityInstanceEntity historicActivityInstanceEntity = historyActivityList.get(0);
        historicActivityInstanceEntity.setAssignee(userId);
        historyActivityDao.updateAssignee(historicActivityInstanceEntity);
    }

    /**
     * 处理分发任务。
     * @param task 任务
     * @param destNodeId 目标id
     * @return boolean
     */
    private boolean handForkTask(TaskEntity task, String destNodeId,FlowElement curActi) {
        String taskId = task.getId();
        boolean isNeedRemoveTran = false;
        //取当前任务的分发令牌
        String token = (String) taskService.getVariableLocal(task.getId(), TaskFork.TAKEN_VAR_NAME);
        if (StringUtil.isEmpty(token)){
            return isNeedRemoveTran;
        }
        TaskFork taskFork = taskForkService.getByInstIdJoinTaskKeyForkToken(task.getProcessInstanceId(), destNodeId, token);

        if (taskFork == null){
            return isNeedRemoveTran;
        }

        //完成次数小于定义分发的次数，需要移除跳转的路径，表示只是完成当前的任务，并不发生跳转。
        if (taskFork.getFininshCount() < taskFork.getForkCount() - 1) {
            //更新完成任务的个数
            taskFork.setFininshCount(taskFork.getFininshCount() + 1);
            taskForkService.update(taskFork);
            //更新Token
            String[] tokenSplits = token.split("[_]");
            if (tokenSplits.length == 2) {
                //若为最外层的汇总，格式如T_1,则需要删除该令牌
                taskService.setVariableLocal(task.getId(), TaskFork.TAKEN_VAR_NAME, null);
            }
            isNeedRemoveTran = true;
        } else {
            String executionId = task.getExecutionId();
            String forkTaskKey = taskFork.getForkTaskKey();
            taskForkService.delById(taskFork.getTaskForkId());
            //更新Token
            String[] tokenSplits = token.split("[_]");
            if (tokenSplits.length == 2) {
                //若为最外层的汇总，格式如T_1,则需要删除该令牌
                taskService.setVariableLocal(task.getId(), TaskFork.TAKEN_VAR_NAME, null);
                String instanceId = task.getProcessInstanceId();
                String topParentExecutionId = "";
                List executionList = runtimeService.createExecutionQuery().processInstanceId(instanceId).list();
                for (Object execution : executionList) {
                    ExecutionEntity ent = (ExecutionEntity)execution;
                    if(null!=ent.getActivityId() && ent.getActivityId().equals(forkTaskKey)){
                        topParentExecutionId = ent.getId();
                        executionDao.updateActId(ent.getId(),curActi.getId());
                        break;
                    }
                }
                //将任务的excution的id更新为主执行流id
                taskDao.updTaskExecution(topParentExecutionId,taskId);
                //删除token变量。
                executionDao.delTokenVarByTaskId(taskId, TaskFork.TAKEN_VAR_NAME);

                executionDao.delVarsByExecutionId(executionId);
                //删除execution。
                executionDao.delExecutionById(executionId);

                executionDao.delSubVarByProcInstId(instanceId);
                executionDao.delSubExecutionByParentId(topParentExecutionId);

            } else if (tokenSplits.length >= 3) {
                //更新token，转换如：T_1_1转成T_1
                String newToken = token.substring(0, token.lastIndexOf("_" + tokenSplits[tokenSplits.length - 1]));
                taskService.setVariableLocal(task.getId(), TaskFork.TAKEN_VAR_NAME, newToken);
            }
        }


        return isNeedRemoveTran;
    }

    /**
     * 跳转到指定节点
     * @param processInstanceId 流程定义id
     * @param currActivityId    当前流程节点id
     * @param destActivityId    目标节点id
     */
    private void jumpToDestNode(String processInstanceId, String currActivityId, String destActivityId) {
        runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
                .moveActivityIdTo(currActivityId, destActivityId).changeState();
    }

    /**
     * 跳转到多个节点
     * @param processInstanceId 流程实例id
     * @param currActivityId 当前节点ID
     * @param destActivityIds 目标节点ID集合
     */
    private void jumpToDestNodes(String processInstanceId, String currActivityId, List<String> destActivityIds) {
        runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
                .moveSingleActivityIdToActivityIds(currActivityId, destActivityIds).changeState();
    }


    /**
     * 产生沟通意见流程任务。
     * @param ent 任务
     * @param users 用户
     * @param sysUser sysUser
     * @return map
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, String> genCommunicationTask(TaskEntity ent, String[] users,SysUser sysUser) {
        if (BeanUtils.isEmpty(users)) {
            return null;
        }
        Map<String, String> map = new HashMap<>(users.length);
        String parentId = ent.getId();
        for (String userId : users) {
            if (userId.equals(sysUser.getUserId())) {
                continue;
            }
            String taskId = UUIDUtils.getUUIDFor32();
            TaskEntity task = (TaskEntity) taskService.newTask(taskId);
            task.setAssignee(userId);
            task.setOwner(userId);
            task.setCreateTime(new Date());
            task.setName(ent.getName());

            task.setParentTaskId(parentId);
            task.setTaskDefinitionKey(ent.getTaskDefinitionKey());
            task.setProcessInstanceId(ent.getProcessInstanceId());
            task.setDescription(TaskOpinion.STATUS_COMMUNICATION.toString());
            task.setProcessDefinitionId(ent.getProcessDefinitionId());
            taskService.saveTask(task);
            map.put(userId,taskId);
        }
        return map;
    }

    /**
     * 新增一个会签任务
     * @param taskId 任务id
     * @param assignee 执行人
     * @return ProcessTask
     */
    @Transactional(rollbackFor = Exception.class)
    public ProcessTask newTask(String taskId, String assignee) {
        return newTask(taskId,assignee,null,null);
    }

    /**
     * 按任务Id，复制另一会签任务出来.
     * <pre>
     * 1.生成流程执行实例。
     * 2.生成任务。
     * 3.增加任务历史。
     * </pre>
     *
     * @param taskId 任务id
     * @param assignee 执行人
     * @param newNodeId 另一个节点id
     * @param newNodeName 另一个节点名称
     * @return ProcessTask
     */
    @Transactional(rollbackFor = Exception.class)
    public ProcessTask newTask(String taskId, String assignee, String newNodeId, String newNodeName) {
        // 生成新的实例id和任务id
        String newExecutionId = idGenerator.getNextId();
        String newTaskId = idGenerator.getNextId();

        // 获取当前任务和实例
        TaskEntityImpl taskEntity = getTask(taskId);
        ExecutionEntity executionEntity = null;
        executionEntity = getExecution(taskEntity.getExecutionId());

        // 复制新的实例
        ProcessExecution newExecution = new ProcessExecution(executionEntity);
        newExecution.setId(newExecutionId);

        // 复制新的任务
        ProcessTask newTask = new ProcessTask();
        BeanUtils.copyNotNullProperties(newTask, taskEntity);
        newTask.setId(newTaskId);
        newTask.setExecutionId(newExecutionId);
        newTask.setCreateTime(new Date());
        newTask.setAssignee(assignee);
        newTask.setOwner(assignee);

        // 创建任务历史
        ProcessTaskHistory newTaskHistory = new ProcessTaskHistory(taskEntity);
        newTaskHistory.setAssignee(assignee);
        newTaskHistory.setStartTime(new Date());
        newTaskHistory.setId(newTaskId);
        newTaskHistory.setOwner(assignee);

        if (newNodeId != null) {
            newExecution.setActivityId(newNodeId);
            newTask.setTaskDefinitionKey(newNodeId);
            newTask.setName(newNodeName);
            newTaskHistory.setTaskDefinitionKey(newNodeId);
            newTaskHistory.setName(newNodeName);
        }

        executionDao.add(newExecution);
        taskDao.insertTask(newTask);
        taskHistoryDao.add(newTaskHistory);
        return newTask;
    }

    /**
     * 产生流程流转任务
     * @param ent 任务
     * @param users 流转人
     * @return map
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, String> genTransToTask(TaskEntity ent, String[] users){
        Map<String, String> map = new HashMap<>(15);
        String parentId = ent.getId();
        for (String userId : users) {
            //产生流传任务
            String taskId = UUIDUtils.getUUIDFor32();
            TaskEntity task = (TaskEntity) taskService.newTask(taskId);
            task.setAssignee(userId);
            task.setOwner(userId);
            task.setCreateTime(new Date());
            task.setName(ent.getName());

            task.setParentTaskId(parentId);
            task.setTaskDefinitionKey(ent.getTaskDefinitionKey());
            task.setProcessInstanceId(ent.getProcessInstanceId());
            task.setDescription(TaskOpinion.STATUS_TRANSTO.toString());
            task.setProcessDefinitionId(ent.getProcessDefinitionId());
            taskService.saveTask(task);
            map.put(userId, taskId);
        }
        return map;
    }

    /**
     * 通过Deployment获取流程定义
     * @param deployId 部署id
     * @return 定义实体
     */
    public ProcessDefinitionEntity getProcessActDefModelByDeployId(String deployId) {
        ProcessDefinition proActDefModel = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();
        if (proActDefModel == null){ return null; }
        return getProcessActDefModelByModelId((proActDefModel.getId()));
    }

    /**
     * 交办
     * @param taskId 任务id
     * @param instanceId 实例id
     * @param userId 用户id
     */
    @Transactional(rollbackFor = Exception.class)
    public void delegate(String taskId,String instanceId,String userId,String memo){
        taskService.setAssignee(taskId,userId);
        ProcessRun processRun = processRunService.getByProcInstanceId(instanceId);
        bpmRunLogService.addRunLog(processRun.getRunId(), BpmRunLog.OPERATOR_TYPE_ASSIGN, memo);
    }

    /**
     * 新建分发任务
     * @param taskEntity 任务实体
     * @param uIdList 执行人
     */
    @Transactional(rollbackFor = Exception.class)
    public void newForkTasks(TaskEntity taskEntity, List<TaskExecutor> uIdList) {
        Set<TaskExecutor> uIdSet = new HashSet<TaskExecutor>();
        uIdSet.addAll(uIdList);
        newForkTasks(taskEntity, uIdSet);
    }

    /**
     * 从一个任务创建相关的的任务。
     * <pre>
     * 1.设置当前任务的任务执行人。
     * 2.其他的用户创建新的任务，excution和历史数据。
     * 		1.添加任务到队列。
     * 		2.添加分发令牌。
     * 			1.如还没有令牌：T_1,T_2,T_3 。
     * 			2.如果任务存在则令牌如下。T_1_1,T_1_2,T_1_3
     * 		3.添加任务意见。
     * </pre>
     *
     * @param taskEntity 用户任务
     * @param uIds       用户Id或组织Id或角色Id或岗位ID
     */
    @Transactional(rollbackFor = Exception.class)
    public void newForkTasks(TaskEntity taskEntity, Set<TaskExecutor> uIds) {
        //获取当前任务的分发的令牌, value as T_1 or T_1_2
        String token = (String) taskEntity.getVariableLocal(TaskFork.TAKEN_VAR_NAME);
        String parentExecutionId = taskEntity.getExecutionId();
        ExecutionEntity parentExecution = getExecution(parentExecutionId);
        if (token == null){
            token = TaskFork.TAKEN_PRE;
        }
        Iterator<TaskExecutor> uIt = uIds.iterator();
        int i = 0;
        while (uIt.hasNext()) {
            if (i++ == 0) {
                //原flowable产生的任务仅需要进行人员授权
                assignTask(taskEntity, uIt.next());
                //加上分发令牌变量
                taskEntity.setVariableLocal(TaskFork.TAKEN_VAR_NAME, token + "_" + i);
                //改变其Execution
                changeTaskExecution(taskEntity,parentExecution);
            } else {
                //扩展产生的流程任务，另需要加上审批意见的记录
                ProcessTask processTask = newTask(taskEntity, uIt.next(),parentExecution);

                TaskEntity newTask = getTask(processTask.getId());
                //加上新的任务至新任务列队，以方便后续的任务回退处理
                TaskThreadService.addTask(newTask);

                //加上分发令牌变量
                taskService.setVariableLocal(processTask.getId(), TaskFork.TAKEN_VAR_NAME, token + "_" + i);

                //添加任务的意见。
                TaskOpinion taskOpinion = new TaskOpinion(processTask);
                taskOpinion.setOpinionId(UUIDUtils.getUUIDFor32());
                taskOpinion.setSuperExecution(processRunService.getSuperProcInstId(processTask.getProcessInstanceId()));
                taskOpinion.setTaskToken(token);

                //配置执行人情况
                String userId = processTask.getOwner();
                if (StringUtil.isNotEmpty(userId)) {
                    SysUser sysUser = sysUserService.getById(userId);
                    taskOpinion.setExeUserId(userId);
                    taskOpinion.setExeFullname(sysUser.getFullName());
                }


                //处理审批意見是否添加父流程Id，如果是外部子流程则添加，否則就不添加为空
                Object isExtCall = runtimeService.getVariable(taskEntity.getExecutionId(), BpmConst.IS_EXTERNAL_CALL);
                // 外部子流程做处理
                if (isExtCall != null) {
                    // 处理如果是外部子流程，运行到外部子流程节点时，审批意见添加父流程的ID
                    ProcessExecution processExecution = executionDao.getById(taskEntity.getProcessInstanceId());
                    // 子流程获取主流程的实例ID
                    if (StringUtil.isEmpty(processExecution.getSuperExecutionId())) {
                        taskOpinion.setSuperExecution(processExecution.getId());
                    } else {
                        ProcessExecution processExecution1 = executionDao.getById(processExecution.getSuperExecutionId());
                        taskOpinion.setSuperExecution(processExecution1.getParentId());
                    }
                }
                taskOpinionService.add(taskOpinion);
            }
        }
    }

    /**
     * 授权任务
     *
     * @param taskEntity 任务实体
     * @param taskExecutor 执行人
     */
    @Transactional(rollbackFor = Exception.class)
    public void assignTask(TaskEntity taskEntity, TaskExecutor taskExecutor) {
        if (TaskExecutor.USER_TYPE_USER.equals(taskExecutor.getType())) {
            taskEntity.setAssignee(taskExecutor.getExecuteId());
            taskEntity.setOwner(taskExecutor.getExecuteId());
            TaskOpinion taskOpinion = taskOpinionService.getByTaskId(taskEntity.getId());
            if(taskOpinion!=null){
                taskOpinion.setExeUserId(taskExecutor.getExecuteId());
                taskOpinionService.update(taskOpinion);
            }
        } else {
            taskEntity.addGroupIdentityLink(taskExecutor.getExecuteId(), taskExecutor.getType());
        }
    }

    /**
     * 改变当前任务对应的exectution
     *
     * @param taskEntity 任务实体
     */
    @Transactional(rollbackFor = Exception.class)
    protected void changeTaskExecution(TaskEntity taskEntity,ExecutionEntity parentExecution) {
        String newExecutionId = idGenerator.getNextId();
        ProcessExecution newExecution =  new ProcessExecution(parentExecution);
        newExecution.setParentId(parentExecution.getId());
        newExecution.setId(newExecutionId);
        executionDao.add(newExecution);
        taskEntity.setExecutionId(newExecutionId);
    }

    /**
     * 新建任务。
     * <pre>
     * 1.添加task。
     * 2.如果用户类型为组时，添加为候选组。
     * 3.添加ProcessExecution。
     * 4.添加任务历史。ProcessTaskHistory
     *
     * </pre>
     *
     * @param taskEntity 任务
     * @param taskExecutor 任务执行人
     * @return ProcessTask
     */
    @Transactional(rollbackFor = Exception.class)
    protected ProcessTask newTask(TaskEntity taskEntity, TaskExecutor taskExecutor,ExecutionEntity parentExecution) {

        String newExecutionId = idGenerator.getNextId();
        String newTaskId = idGenerator.getNextId();

        ProcessExecution newExecution = new ProcessExecution(parentExecution);
        newExecution.setParentId(parentExecution.getId());
        newExecution.setId(newExecutionId);
        ProcessTask newTask = new ProcessTask();
        BeanUtils.copyNotNullProperties(newTask, taskEntity);
        newTask.setId(newTaskId);
        newTask.setExecutionId(newExecutionId);
        newTask.setCreateTime(new Date());
        ProcessTaskHistory newTaskHistory = new ProcessTaskHistory(taskEntity);
        TaskUser taskUser = null;

        String executorId = taskExecutor.getExecuteId();
        //若为用户类型时
        if (TaskExecutor.USER_TYPE_USER.equals(taskExecutor.getType())) {
            newTask.setAssignee(executorId);
            newTask.setOwner(executorId);
            newTaskHistory.setAssignee(executorId);
            newTaskHistory.setOwner(executorId);
        } else {
            taskUser = new TaskUser();
            taskUser.setId(idGenerator.getNextId());
            taskUser.setGroupId(executorId);
            taskUser.setType(taskExecutor.getType());
            taskUser.setReversion(1);
            taskUser.setTaskId(newTaskId);
            taskUser.setUserId(BpmConst.EMPTY_USER);
        }

        newTaskHistory.setStartTime(new Date());
        newTaskHistory.setExecutionId(newExecutionId);
        newTaskHistory.setId(newTaskId);

        executionDao.add(newExecution);
        taskDao.insertTask(newTask);
        taskHistoryDao.add(newTaskHistory);
        if (taskUser != null) {
            taskUserDao.add(taskUser);
        }

        return newTask;
    }

    //==================================封装flowable方法-开始=============================================//

    /**
     * 获取正在运行中的任务
     * @param taskId 任务id
     * @return 任务实体
     */
    public TaskEntityImpl getTask(String taskId) {
        TaskEntityImpl taskEntity = (TaskEntityImpl) taskService.createTaskQuery().taskId(taskId).singleResult();
        return taskEntity;
    }

    /**
     * 根据流程实例id获取正在运行中的任务
     * @param procInstId 流程实例id
     * @return 任务列表
     */
    public List<Task> queryTasksByProcInstId(String procInstId) {
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(procInstId).list();
        return taskList;
    }

    /**
     * 根据任务id获取任务流程变量
     * @param taskId 任务id
     * @return 流程变量
     */
    public Map<String, Object> getVarsByTaskId(String taskId) {
        return taskService.getVariables(taskId);
    }

    /**
     * 添加任务流程变量
     * @param taskId 任务id
     * @param variableName 变量名称
     * @param varVal 变量值
     */
    public void setTaskVariable(String taskId, String variableName, Object varVal) {
        taskService.setVariable(taskId, variableName, varVal);
    }

    /**
     * 根据执行流id获取正在运行的执行流信息
     * @param executionId 执行流id
     * @return 执行流实体
     */
    public ExecutionEntity getExecution(String executionId) {
        Execution execution = runtimeService.createExecutionQuery().executionId(executionId).singleResult();
        return (ExecutionEntity) execution;
    }

    /**
     * 根据流程定义ID启动流程。
     * @param porcessDefId 流程定义ID。
     * @param businessKey  主键。
     * @param variables    流程变量。
     * @return 流程实例
     */
    public ProcessInstance startFlowById(String porcessDefId, String businessKey, Map<String, Object> variables) {
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(porcessDefId, businessKey, variables);
        return processInstance;
    }

    /**
     * 按流程定义Key进行启动流程。
     *
     * @param processDefKey 流程定义KEY
     * @param businessKey   主键
     * @param variables     流程变量。
     * @return ProcessInstance
     */
    public ProcessInstance startFlowByKey(String processDefKey, String businessKey, Map<String, Object> variables) {
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefKey, businessKey, variables);
        return processInstance;
    }

    /**
     * 根据流程实例id获取流程实例
     * @param procInstId 流程实例id
     * @return 流程实例
     */
    public ProcessInstance getProcessInstance(String procInstId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(procInstId).singleResult();
        return processInstance;
    }

    /**
     * 查看某个流程实例的所有历史任务
     * @param processInstanceId 流程实例id
     * @return 历史任务列表
     */
    public List<HistoricTaskInstance> getHistoryTasks(String processInstanceId) {
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).list();
        return list;
    }

    /**
     * 根据任务id查询历史任务
     * @param taskId 任务id
     * @return 历史任务实体
     */
    public HistoricTaskInstanceEntity getHistoricTaskInstanceEntity(String taskId) {
        return (HistoricTaskInstanceEntity) historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
    }

    /**
     * 根据实例id获取历史实例
     * @param procInstanceId 实例id
     * @return 历史实例实体
     */
    public HistoricProcessInstanceEntity getHistoryInstanceByInstanceId(String procInstanceId){
        return (HistoricProcessInstanceEntity)historyService.createHistoricProcessInstanceQuery().processInstanceId(procInstanceId).singleResult();
    }

    /**
     * 根据流程定义id获取流程定义实体
     * @param processDefinitionId 流程定义id
     * @return 流程定义实体
     */
    public ProcessDefinitionEntity getProcessDefinitionEntity(String processDefinitionId) {
        ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(processDefinitionId);
        return processDefinition;
    }

    /**
     *  通过流程定义Id获取流程定义实体
     * @param defId 定义id
     * @return 任务实体
     */
    private ProcessDefinitionEntity getProcessActDefModelByModelId(String defId) {
        ProcessDefinitionEntity ent = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(defId);
        return ent;
    }


    /**
     * 获取指定节点之前的节点
     * @param procDefId 流程定义id
     * @param nodeId 节点id
     * @return 节点信息集合
     */
    public Set<ActNode> getPreNodes(String procDefId, String nodeId) {
        Map<String, FlowNodeInfo> nodeInfoMap = NodeCache.getByProcDefId(procDefId);
        FlowNodeInfo nodeInfo = nodeInfoMap.get(nodeId);
        Set<ActNode> preNodeSet = new HashSet<>();
        getCyclePreNodes(nodeInfo, preNodeSet);
        return preNodeSet;
    }

    /**
     * 递归获取之前节点
     * @param nodeInfo 节点信息
     * @param preNodeSet 节点信息集合
     */
    private void getCyclePreNodes(FlowNodeInfo nodeInfo, Set<ActNode> preNodeSet) {
        List<FlowNodeInfo> preFlowNodesInfo = nodeInfo.getPreFlowNodesInfo();
        if (preFlowNodesInfo.size() == 1 && FlowNodeInfo.TYPE_START_EVENT.equals(preFlowNodesInfo.get(0).getNodeType())) {
            return;
        }

        for (FlowNodeInfo preNode : preFlowNodesInfo) {
            if (FlowNodeInfo.TYPE_USERTASK.equals(preNode.getNodeType())) {
                preNodeSet.add(new ActNode(preNode.getNodeId(), preNode.getNodeName()));
            }
            getCyclePreNodes(preNode, preNodeSet);
        }
    }
    //==================================封装flowable方法-结束=============================================//
}
